//
// HTTPPost
//
// Copyright (C) jextra.net.
//
//  This file is part of the Crack build system.
//
//  The Crack build system is free software; you can redistribute it 
//  and/or modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  The Crack build system is distributed in the hope that it will be 
//  useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with the Crack build system; if not, write to the Free
//  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
//  02111-1307 USA.
//

package net.jextra.crack.tool;

import java.io.*;
import java.net.*;
import java.util.*;

/**
 * <p>
 * This class helps to send POST HTTP requests with various form data,
 * including files. Cookies can be added to be included in the request.
 * </p>
 *
 * @author Vlad Patryshev
 * @version 1.0
 */
public class HTTPPost
{
    // ============================================================
    // Fields
    // ============================================================

    public static final int BUFFER_SIZE = 10000;

    private static Random _random = new Random();

    private URLConnection connection;
    private OutputStream out;
    private Map<String,String> cookies;
    private String boundary;

    // ============================================================
    // Constructors
    // ============================================================

    /**
     * Creates a new multipart POST HTTP request on a freshly opened URLConnection
     */
    public HTTPPost( URLConnection connection )
        throws IOException
    {
        this.connection = connection;
        boundary = "---------------------------" + createRandomString() + createRandomString() +
            createRandomString();
        cookies = new HashMap<String,String>();
        connection.setDoOutput( true );
        connection.setRequestProperty( "Content-Type", "multipart/form-data; boundary=" +
            boundary );
    }

    // ============================================================
    // Methods
    // ============================================================

    // ----------
    // public
    // ----------

    public void addCookie( String name, String value )
        throws IOException
    {
        cookies.put( name, value );
    }

    public void postCookies()
    {
        StringBuilder cookieList = new StringBuilder();

        for ( String key : cookies.keySet() )
        {
            if ( cookieList.length() > 0 )
            {
                cookieList.append( "; " );
            }
            cookieList.append( key + "=" + cookies.get( key ) );
        }
        if ( cookieList.length() > 0 )
        {
            connection.setRequestProperty( "Cookie", cookieList.toString() );
        }
    }

    public void addParameter( String name, File file )
        throws IOException
    {
        addParameter( name, file.getPath(), new FileInputStream( file ), file.length() );
    }

    public void addParameter( String name, String filename, InputStream in, long inSize )
        throws IOException
    {
        writeBoundary();
        writeName( name );
        write( "; filename=\"" );
        write( filename );
        write( '"' );
        writeNewline();
        write( "Content-Type: " );
        String type = URLConnection.guessContentTypeFromName( filename );
        if ( type == null )
        {
            type = "application/octet-stream";
        }
        writeln( type );
        writeNewline();
        pipe( in, inSize, getOut() );
        writeNewline();
    }

    public void addParameter( String name, String value )
        throws IOException
    {
        writeBoundary();
        writeName( name );
        writeNewline();
        writeNewline();
        writeln( value );
    }

    public InputStream execute()
        throws IOException
    {
        writeBoundary();
        writeln( "--" );
        getOut().close();
        return connection.getInputStream();
    }

    // ----------
    // private
    // ----------

    private OutputStream getOut()
        throws IOException
    {
        if ( out == null )
        {
            out = connection.getOutputStream();
        }

        return out;
    }

    private void write( char c )
        throws IOException
    {
        getOut().write( c );
    }

    private void write( String s )
        throws IOException
    {
        getOut().write( s.getBytes() );
    }

    private void writeln( String s )
        throws IOException
    {
        write( s );
        writeNewline();
    }

    private void writeNewline()
        throws IOException
    {
        write( "\r\n" );
    }

    private static String createRandomString()
    {
        return Long.toString( _random.nextLong(), 36 );
    }

    private void writeBoundary()
        throws IOException
    {
        write( "--" );
        write( boundary );
    }

    private void writeName( String name )
        throws IOException
    {
        writeNewline();
        write( "Content-Disposition: form-data; name=\"" );
        write( name );
        write( '"' );
    }

    private void pipe( InputStream in, long inSize, OutputStream out )
        throws IOException
    {
        synchronized( in )
        {
            byte[] buffer = new byte[BUFFER_SIZE];
            int len = 0;
//            long total = 0;
            while ( ( len = in.read( buffer, 0, buffer.length ) ) >= 0 )
            {
                out.write( buffer, 0, len );
//                total += len;
            }
        }
        out.flush();
    }
}

